#include <adwin/ADwinSystem.h>
#include <adwin/ADwinSystemException.h>
#include <src/ADwin_V2.h>

#include <vector>

namespace ADwin {
namespace Driver {
struct ADwinSystemException::Impl {
  Impl() : _errorNumber(0) {}

  Impl(int errorNumber) : _errorNumber(errorNumber) {}

  int _errorNumber;
  std::map<std::string, std::string> _data;
};

ADwinSystemException::ADwinSystemException()
    : std::runtime_error("ADwin System Exception"), _impl(new Impl()) {}

ADwinSystemException::ADwinSystemException(int errorNumber)
    : std::runtime_error(GetErrorText(errorNumber) + " (Error number " +
                         std::to_string(errorNumber) + ")"),
      _impl(new Impl(errorNumber)) {}

ADwinSystemException::ADwinSystemException(const std::string& message)
    : std::runtime_error(message), _impl(new Impl()) {}

ADwinSystemException::ADwinSystemException(
    int errorNumber, const std::map<std::string, std::string>& dataEntries)
    : std::runtime_error(GetErrorText(errorNumber) + " (Error number " +
                         std::to_string(errorNumber) + ")"),
      _impl(new Impl(errorNumber)) {
  for (const auto& entry : dataEntries) {
    _impl->_data[entry.first] = entry.second;
  }
}

ADwinSystemException::ADwinSystemException(
    const std::string& message,
    const std::map<std::string, std::string>& dataEntries)
    : std::runtime_error(message), _impl(new Impl) {
  for (const auto& entry : dataEntries) {
    _impl->_data[entry.first] = entry.second;
  }
}

ADwinSystemException::ADwinSystemException(const std::string& message,
                                           const std::exception& innerException)
    : std::runtime_error(message + ": " + innerException.what()),
      _impl(new Impl) {}

std::string ADwinSystemException::GetErrorText(long errorNumber) {
  int count = 250;
  std::vector<char> buffer(count);
  long error = 0;
  Set_Language(static_cast<long>(ADwinSystem::GetLanguage()), &error);
  Get_Error_Text(errorNumber, const_cast<char*>(buffer.data()), buffer.size());
  return std::string(buffer.data());
}

int ADwinSystemException::ErrorNumber() const { return _impl->_errorNumber; }

const std::map<std::string, std::string>& ADwinSystemException::Data() const {
  return _impl->_data;
}

void ADwinSystemException::AddData(const std::string& key,
                                   const std::string& value) {
  _impl->_data[key] = value;
}

ADwinSystemException::~ADwinSystemException() = default;
}  // namespace Driver
}  // namespace ADwin